#ifndef _SYNCOMPRESSOR_H
#define _SYNCOMPRESSOR_H

#include <math.h>	// for min(), max()
#include "syncommon.h"

#define sampleRate_ INTERNAL_SAMPLE_RATE

namespace chunkware_simple {

// DC offset (to prevent denormal)
// USE:
// 1. init envelope state to DC_OFFSET before processing
// 2. add to input before envelope runtime function
static const float DC_OFFSET = 1.0E-25f;

class EnvelopeDetector {
public:
	// runtime function
	inline void run(float in, float &state) {
		setCoef();
		state = in + coef_ * (state - in);
	}
	float ms;				// time constant in ms
protected:
	void setCoef(void) {
		coef_ = expf(-1000.0f / (ms * sampleRate_));
	}
	float coef_;			// runtime coefficient
};	// end SimpleComp class

class AttRelEnvelope {
public:
	void setAttack(float ms)  { att_.ms = ms; }
	void setRelease(float ms) { rel_.ms = ms; }
	// runtime function
	inline void run(float in, float &state) {
		// assumes that  positive delta = attack  * negative delta = release
		// good for linear & log values	
		if (in > state) {
			att_.run(in, state);	// attack
		} else {
			rel_.run(in, state);	// release
		}
	}
private:	
	EnvelopeDetector att_;
	EnvelopeDetector rel_;
};	// end AttRelEnvelope class

// simple compressor
class SimpleComp : public AttRelEnvelope {
public:
	SimpleComp();
	// runtime
	void initRuntime(void);			// call before runtime (in resume())
	void process(float &in1, float &in2);	// compressor runtime process
	void process(float &in1, float &in2, float keyLinked);	// with stereo-linked key in
	// transfer function
	float threshdB;		// threshold (dB)
	float ratio;			// ratio (compression: < 1 ; expansion: > 1)
private:
	// runtime variables
	float envdB_;			// over-threshold envelope (dB)
};	// end SimpleComp class

// simple compressor with RMS detection
class SimpleCompRms : public SimpleComp {
public:
	SimpleCompRms();
	void setWindow(float ms) {
		// RMS window
		ave_.ms = ms;
	}
	void initRuntime(void);			// call before runtime (in resume())
	void processRms(float &in1, float &in2);	// compressor runtime process
	void Render(Block leftBuf, Block rightBuf, float makeup, bool rms) {
		FORBLOCK(i) {
			float l = leftBuf[i], r = rightBuf[i];
			if (rms) 
				processRms(l, r);
			else
				process(l, r);
			leftBuf[i]  = (float)l * makeup;
			rightBuf[i] = (float)r * makeup;
		}
	}
private:
	EnvelopeDetector ave_;	// averager
	float aveOfSqrs_;		// average of squares
};	// end SimpleCompRms class

}	// end namespace chunkware_simple

#endif